﻿using System;
using System.Collections.Generic;
using System.Text;
using Com.Toook.AutoDownloadHtml.DownloadHtml.ParsePage.Entity;
using Com.Toook.AutoDownloadHtml.DownloadHtml.ParsePage.Enum;
using System.Text.RegularExpressions;
using Com.Toook.AutoDownloadHtml.Util;
using NSoup.Select;
using NSoup.Nodes;

namespace Com.Toook.AutoDownloadHtml.DownloadHtml.ParsePage
{
    public class ParseElements
    {
        /// <summary>
        /// 按照Rule中的Step规则，解析出符合条件的元素列表。供Property获取最终结果用
        /// </summary>
        /// <param name="elements"></param>
        /// <param name="rule"></param>
        /// <returns></returns>
        public static Elements GetElementsByStep(Elements elements, Rule rule)
        {
            //elements.Count > 0 判断 GetAndMergeElements 返回的initElements新结果集是否有数据，没有就跳出循环
            while (rule.NextStep() && elements.Count > 0)
            {
                //按照step.GetBy规则，遍历elements，合并每个节点的结果。将新结果赋给elements，迭代下一个step操作。
                elements = ParseElements.GetAndMergeElements(elements, rule.CurrentStep);
            }
            rule.ResetNextStep();
            return elements;
        }

        /// <summary>
        /// 遍历elements节点，获取每个节点中符合step条件的子节点集合；合并所有子节点集合，作为最终的results结果集
        /// </summary>
        /// <param name="elements"></param>
        /// <param name="step"></param>
        /// <returns></returns>
        public static Elements GetAndMergeElements(Elements elements, Step step)
        {
            Elements results = new Elements();

            foreach (Element e in elements)
            {
                Elements eles = ParseElements.GetElements(step, e);
                foreach (Element r in eles)
                {
                    results.Add(r);//将满足Step条件的元素添加到结果集                 
                }
            }
            return results;
        }
        /// <summary>
        /// 获取满足step条件的元素集合
        /// </summary>
        /// <param name="step"></param>
        /// <param name="element"></param>        
        public static Elements GetElements(Step step, Element element)
        {
            Elements results = new Elements();
            int index;
            switch (step.GetBy)
            {
                case GetBy.Id:
                    Element ele = element.GetElementById(step.Name);
                    if (ele != null && ele.ChildNodes.Count > 0)
                    {
                        results.Add(ele);
                    }
                    break;
                case GetBy.NextElementSibling:
                    Element eleNext = element.NextElementSibling;
                    if (eleNext != null)
                    {
                        results.Add(eleNext);
                    }
                    break;
                case GetBy.PreviousElementSibling:
                    Element elePre = element.PreviousElementSibling;
                    if (elePre != null)
                    {
                        results.Add(elePre);
                    }
                    break;
                case GetBy.Tag:
                    results = element.GetElementsByTag(step.Name);
                    break;
                case GetBy.Class:
                    results = element.GetElementsByClass(step.Name);
                    break;
                case GetBy.ContainText:
                    results = element.GetElementsContainingText(step.Name);
                    break;
                case GetBy.ContainOwnText:
                    results = element.GetElementsContainingOwnText(step.Name);
                    break;
                case GetBy.MatchText:
                    results = element.GetElementsMatchingText(step.Name);
                    break;
                case GetBy.MatchOwnText:
                    results = element.GetElementsMatchingOwnText(step.Name);
                    break;
                case GetBy.Attr:
                    results = element.GetElementsByAttribute(step.Name);
                    break;
                case GetBy.AttrStart:
                    results = element.GetElementsByAttributeStarting(step.Name);
                    break;
                case GetBy.IndexEquals:
                    int.TryParse(step.Name, out index);
                    results = element.GetElementsByIndexEquals(index);
                    break;
                case GetBy.IndexLessThan:
                    int.TryParse(step.Name, out index);
                    results = element.GetElementsByIndexLessThan(index);
                    break;
                case GetBy.IndexGreaterThan:
                    int.TryParse(step.Name, out index);
                    results = element.GetElementsByIndexGreaterThan(index);
                    break;
                //属性名，属性值
                case GetBy.AttrValue:
                    results = element.GetElementsByAttributeValue(step.Name, step.Value);
                    break;
                case GetBy.AttrValueStart:
                    results = element.GetElementsByAttributeValueStarting(step.Name, step.Value);
                    break;
                case GetBy.AttrValueEnd:
                    results = element.GetElementsByAttributeValueEnding(step.Name, step.Value);
                    break;
                case GetBy.AttrValueContain:
                    results = element.GetElementsByAttributeValueContaining(step.Name, step.Value);
                    break;
                case GetBy.AttrValueMatch:
                    results = element.GetElementsByAttributeValueMatching(step.Name, step.Value);
                    break;
                case GetBy.AttrValueContainDateNow:
                    string date = DateTime.Now.ToString(step.Value);//通过step.Value指定的格式，格式化当前日期
                    results = element.GetElementsByAttributeValueContaining(step.Name, date);
                    break;

                case GetBy.RemoveTag:
                    Elements tagElements = element.GetElementsByTag(step.Name);
                    foreach (Element tag in tagElements)
                    {
                        tag.Remove();
                    }

                    results.Add(element);
                    break;
                default:
                    LogHelper.Log.Error("GetElements(Step step, Element element) GetBy=" + step.GetBy + "不合法。");
                    break;
            }
            //应用Step的过滤条件
            results = ParseElements.GetElementByFilterList(step.FilterList, results);
            return results;
        }

        /// <summary>
        /// 对所有Elements应用List&lt;Filter&gt;过滤条件。没有满足的results.Count == 0
        /// </summary>
        /// <param name="filterList"></param>
        /// <param name="elements"></param>
        /// <returns>results已经new初始化, 不会为空。没有满足的results.Count == 0</returns>
        public static Elements GetElementByFilterList(List<Filter> filterList, Elements elements)
        {
            Elements results = new Elements();
            
            if (filterList != null && filterList.Count > 0)
            {
                int filterCount = filterList.Count - 1;
                int i = 0;
                foreach (Filter filter in filterList)//使用每个过滤条件
                {
                    foreach (Element ele in elements)//过滤出符合条件的元素
                    {
                        Element e = ParseElements.GetElementByStepFilter(filter, ele);
                        if (e != null)
                        {
                            results.Add(e);
                        }
                    }

                    if (results.Count == 0)
                    {
                        break;//没有满足条件的元素
                    }
                    else
                    {
                        elements = results;//将过滤后的结果赋值给elements，继续过滤
                        if (i < filterCount)//如果不是最后一个Filter,清空结果，进行下一步过滤
                        {
                            results.Clear();
                        }
                    }
                    i++;
                }
            }
            else
            {
                return elements;
            }
            return results;
        }
        /// <summary>
        /// 判断元素是否满足Step的Filter过滤条件。满足原样返回，不满足返回null
        /// </summary>
        /// <param name="Filter"></param>
        /// <param name="element"></param>
        /// <returns>满足原样返回，不满足返回null</returns>
        public static Element GetElementByStepFilter(Filter filter, Element element)
        {
            Element result = null;
            
            switch (filter.GetBy)
            {
                case GetBy.StepFilterHasAttr:
                    result = element.HasAttr(filter.Name) ? element : null;
                    break;
                case GetBy.StepFilterAttrContains:
                    if (element.HasAttr(filter.Name))
                    {
                        result = element.Attr(filter.Name).Contains(filter.Value) ? element : null;
                    }
                    break;
                case GetBy.StepFilterHasClass:
                    result = element.HasClass(filter.Name) ? element : null;
                    break;
                case GetBy.StepFilterOwnTextContains:
                    result = element.OwnText().Contains(filter.Name) ? element : null;
                    break;
                case GetBy.StepFilterTextContains:
                    result = element.Text().Contains(filter.Name) ? element : null;
                    break;
                case GetBy.StepFilterHtmlContains:
                    result = element.Html().Contains(filter.Name) ? element : null;
                    break;
                case GetBy.StepFilterValContains:
                    result = element.Val().Contains(filter.Name) ? element : null;
                    break;
                case GetBy.RemoveTag:
                    Elements tagElements = element.GetElementsByTag(filter.Name);
                    foreach (Element tag in tagElements)
                    {
                        tag.Remove();
                    }
                    result = element;
                    break;

                default:
                    LogHelper.Log.Error("FilterStepElement() GetBy=" + filter.GetBy + "不合法。");
                    break;
            }
            return result;
        }

        /// <summary>
        /// 根据property的result节点获取结果字符串
        /// </summary>
        /// <param name="elements"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static string GetResultStringByResult(Elements elements, XmlResult result)
        {
            string str = null;
            if (elements == null || elements.Count < 1)
            {
                return str;
            }

            switch (result.GetBy)
            {
                case GetBy.ResultInAttr:
                    str = elements[0].Attr(result.Name);
                    break;
                case GetBy.ResultInText:
                    str = elements[0].Text();
                    break;
                case GetBy.ResultInHtml:
                    str = elements[0].Html();
                    break;
                default:
                    break;
            }
            if (result.ResultFilter != null && result.ResultFilter.Count > 0)
            {
                str = ParseElements.GetResultStringByFilter(str, result.ResultFilter);
            }

            return StringHelper.RemoveMoreWhitespace(str);//删除字符串中多余的空格，只留1个         
        }

        /// <summary>
        /// 从Tag的字符串[ 属性 / Text / Html ]中获取最终的结果字符串
        /// </summary>
        /// <param name="elements"></param>
        /// <param name="step"></param>
        /// <returns></returns>
        public static string GetResultStringByFilter(string tagString, List<Filter> filterList)
        {
            if (filterList == null || filterList.Count < 1)
            {
                return tagString;//直接返回
            }

            string result = tagString;
            foreach (Filter filter in filterList)
            {
                if (filter.GetBy == GetBy.ResultFilterBetween)//获取指定字符串之间的字符串，不包含字符串本身
                {
                    int start = result.IndexOf(filter.Name);
                    if (start != -1)
                    {
                        int end = result.IndexOf(filter.Name, start);
                        if (end != -1)
                        {
                            result = result.Substring(start + filter.Name.Length, end - start - filter.Name.Length);
                        }
                    }
                }
                else if (filter.GetBy == GetBy.ResultFilterReplace)//删除/替换
                {
                    result = result.Replace(filter.Name, filter.Value);
                }
                else if (filter.GetBy == GetBy.ResultFilterMatch)//通过正则表达式获取结果字符串
                {
                    Regex reg = new Regex(filter.Value, RegexOptions.IgnoreCase);
                    Match match = reg.Match(result);
                    if (match.Success)
                    {
                        result = match.Result("${result}");
                    }
                }
            }

            return result;
        }
    }
}
